﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Windows.Forms;

namespace ChipSharp
{
    class Game : CharpThread
    {
    /* .. types */
        public enum CommandType
        {
            kMove,
            kStop,
            kDie,
        }

        public struct UserCommand
        {
            public CommandType Type;
            public int Param;

            public UserCommand(CommandType type, int param)
            {
                Type = type;
                Param = param;
            }
        }

    /* .. properties */

        public World World
        {
            get { return myWorld; }
        }

        public Panel Panel
        {
            get { return myPanel; }
        }

        public int TimeSpan
        {
            get; 
            set;
        }
        public int TimeInc
        {
            get;
            set;
        }

    /* .. construction and destruction */
        public Game(ContainerControl form, Renderer renderer)
            : base(true)
        {
            myRenderer = renderer;
            myForm = form;

            myWorld = new World(this, renderer);
            myPanel = new Panel(this, renderer);
            TimeSpan = 30;
            TimeInc = 1;
        }

        ~Game()
        {
        }

    /* .. game commands */
        public void SendCommand(CommandType type)
        {
            UserCommand cmd = new UserCommand(type, 0);
            myUserCmds.Enqueue(cmd);
        }

        public void SendCommand(CommandType type, int param)
        {
            UserCommand cmd = new UserCommand(type, param);
            myUserCmds.Enqueue(cmd);
        }


    /* .. user commands */
        public void ManMove(World.Direction dir)
        {
            UserCommand cmd = new UserCommand(CommandType.kMove, (int)dir);
            myUserCmds.Enqueue(cmd);
        }

        public void ManStop()
        {
            UserCommand cmd = new UserCommand(CommandType.kStop, (int)0);
            myUserCmds.Enqueue(cmd);
        }

    /* command handler */
        private void HandleCommand()
        {
            if (myUserCmds.Count == 0)
            {
                return;
            }
            UserCommand cmd = myUserCmds.Dequeue();

            switch (cmd.Type)
            {
                case CommandType.kDie:
                    //myForm.Close();

                    break;
                case CommandType.kMove:
                    World.Direction dir = (World.Direction)cmd.Param;
                    myWorld.Man.StartMoving(dir);
                    break;
                case CommandType.kStop:
                    myWorld.Man.StopMoving();
                    break;
            }
        }

    /* .. game loop */
        protected int FirstFrame()
        {
            mIniMSec = System.Environment.TickCount;
            return 0;
        }

        protected int WaitForNextFrame()
        {
            int iFrame;
            int dueMSec;
            int currMSec = System.Environment.TickCount;

            if (currMSec < mIniMSec)
            {
                throw new ApplicationException("Exceeding Max Game Time");
            }

            iFrame = (currMSec - mIniMSec) * TimeSpan / (1000 * TimeInc) + 1;
            dueMSec = mIniMSec + iFrame * TimeInc * 1000 / TimeSpan;

            if (dueMSec > currMSec)
            {
                int d = dueMSec - currMSec;
                Thread.Sleep(d);
            }

            return iFrame;
        }

        protected override void Run()
        {
            while (myWorld == null || myRenderer == null)
            {
                Thread.Sleep(50);
            }

            int iFrame = FirstFrame();
            int iLastFrame = iFrame - 1;

            while (true)
            {
                /* command and user input */
                HandleCommand();

                /* physical states */
                for (; iLastFrame < iFrame; iLastFrame++)
                {
                    myWorld.UpdateObjects();
                }

                /* drawing */
                myWorld.UpdateView();
                myWorld.Redraw();           // redraw the world
                myRenderer.Invalidate();    // invoke the render for the entire game system

                /* timing */
                iFrame = WaitForNextFrame();
            }
        }

    /* .. serialization */
        public void Load(Stream stream)
        {
            throw new NotImplementedException();
        }

        public void Save(Stream stream)
        {
            throw new NotImplementedException();
        }

    /* .. private  */
        protected ContainerControl myForm = null;
        protected Queue<UserCommand> myUserCmds = new Queue<UserCommand>();
        protected Renderer myRenderer = null;

        protected World myWorld = null;
        protected Panel myPanel = null;

        protected int mIniMSec = 0;

    }
}
